﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Dycox.Linq
{
	/// <summary>
	/// 提供了一组基于<see cref="System.Linq.IQueryable"/>的扩展方法用于查询和分页。
	/// </summary>
	public static class LinqExtension
	{

        public static QueryResult Execute<T>(this IQueryable<T> query, QueryArgs args)
        {
            return Execute<T>(query, args, null);
        }

        public static QueryResult Execute<T>(this IQueryable<T> query, QueryArgs args, BuildCustomQuery<T> buildCustomProperty)
        {
            QueryExecutor qe = new QueryExecutor(args);

            return qe.Execute(query, buildCustomProperty);
        }

		/// <summary>
		/// 对数据进行分页。
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="query"></param>
		/// <param name="pagingInfo"></param>
		/// <returns></returns>
		public static IQueryable<T> Page<T>(this IQueryable<T> query, PagingInfo pagingInfo)
			//where T : class
		{
            if (query == null)
                throw new ArgumentNullException("query");

			if (pagingInfo != null)
				return pagingInfo.Page(query);
			else
				return query;
		}

        /// <summary>
        /// 对数据进行分页。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="pagingInfo"></param>
        /// <returns></returns>
        public static IEnumerable<T> Page<T>(this IEnumerable<T> query, PagingInfo pagingInfo)
        //where T : class
        {
            if (query == null)
                throw new ArgumentNullException("query");
            
            if (pagingInfo != null)
                return pagingInfo.Page(query);
            else
                return query;
        }

        public static bool TryPop<T>(this Stack<T> stack, out T value, Func<T, bool> predicate)
        {
            if (stack == null)
                throw new ArgumentNullException("stack");
            if (predicate == null)
                throw new ArgumentNullException("predicate");

            if (stack.Count > 0)
            {
                T obj = stack.Pop();
                if (predicate(obj))
                {
                    value = stack.Pop();
                    return true;
                }
            }

            value = default(T);
            return false;
        }

        public static IQueryable<T> InRange<T, T1>(this IQueryable<T> query, Expression<Func<T, T1>> selector, IRange range)
        {
            Expression expr =  QueryExecutor.BuildBetweenExpression(selector.Body, range, selector.ReturnType);
            
            if (expr != null)
            {
                //ParameterExpression p = selector.Parameters[0];

                //if (p == null)
                //    throw new NotSupportedException(SR.GetString("static_member_is_not_supported"));

                query = query.Where(Expression.Lambda<Func<T, bool>>(expr, selector.Parameters));
            }

            return query;
        }

        private static ParameterExpression GetParameter(MemberExpression mexpr)
        {
            Expression expr = mexpr.Expression;

            if (expr is ParameterExpression)
                return (ParameterExpression)expr;
            else if (expr is MemberExpression)
                return GetParameter((MemberExpression)expr);
            else if (expr == null)
                return null;
            else
                throw new InvalidOperationException(SR.GetString("cannot_find_parameter"));
                
        }

        /// <summary>
        /// 从指定的Lambda表达式中提取其指定的类型成员。
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">如果表达式不是对成员的访问时引发此异常。</exception>
        internal static MemberExpression GetMemberFromLambda(LambdaExpression lambda)
        {
            if (lambda == null)
                throw new ArgumentNullException("lambda");

            Expression body = lambda.Body;

            return GetMemberFromExpression(body);
        }

        internal static MemberExpression GetMemberFromExpression(Expression expr)
        {
            if ((expr != null) && ((expr.NodeType == ExpressionType.Convert) || (expr.NodeType == ExpressionType.ConvertChecked)))
            {
                expr = ((UnaryExpression)expr).Operand;
            }
            MemberExpression expression2 = expr as MemberExpression;
            if ((expression2 == null))// || (expression2.Expression.NodeType != ExpressionType.Parameter))
            {
                throw new ArgumentException(SR.GetString("invalid_member_specified"));
            }
            return expression2;
        }


        /// <summary>
        /// 从指定的Lambda表达式中提取成员列表。该表达式必须至少具有一个传入参数，表达式的语句是一个一维数组的初始化语句或匿名类型的构造函数。
        /// 此方法已包含了对<see cref="M:GetMemberFromLambda(LambdaExpression)"/>的调用。
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        internal static MemberInfo[] GetMembersFromLambda(LambdaExpression lambda)
        {
            if (lambda == null)
                throw new ArgumentNullException("lambda");

            Expression body = lambda.Body;
            IEnumerable<Expression> list = null;

            if (body != null)
            {
                var nodeType = body.NodeType;

                if (nodeType == ExpressionType.NewArrayInit)
                {
                    list = ((NewArrayExpression)body).Expressions;
                }
                else if (nodeType == ExpressionType.New)
                {
                    list = ((NewExpression)body).Arguments;
                }
                else
                {
                    list = new[] { body };
                }
            }

            if (list == null)
                throw new ArgumentException(SR.GetString("invalid_member_specified"));

            return list.Select(o => GetMemberFromExpression(o).Member).ToArray();
        }

    }
}
